package ffte.core.mixed

import spinal.core._
import spinal.lib._

import ffte.types.VecComplex
import ffte.property.{getFFTIW,getFFTOW,FFTIOConfig}
import ffte.core.{combine}
import ffte.core.flat.Flat
import ffte.core.streamed.{Streamed,streamedComponents}
import ffte.algorithm.FFTGen.{Method}

/**
 * Mixed-radix FFT components trait
 *
 * This trait provides the foundation for mixed-radix FFT implementations that
 * combine both flat (combinatorial) and streamed (pipelined) architectures.
 * The mixed approach optimizes resource usage by applying the most appropriate
 * implementation style to each stage of the FFT decomposition.
 *
 * Architecture Philosophy:
 * The mixed radix approach uses different implementation strategies for
 * different stages based on their computational characteristics:
 * - Flat architecture: Better for smaller, simpler transforms
 * - Streamed architecture: Better for larger, more complex transforms
 * - Adaptive selection: Chooses optimal architecture per stage
 *
 * Performance Benefits:
 * - Reduced resource usage compared to fully flat implementations
 * - Improved throughput compared to fully streamed implementations
 * - Flexible trade-offs between latency and resource consumption
 * - Better timing closure through balanced computational load
 *
 * Implementation Strategy:
 * - First stage: Uses flat or streamed based on rgen flag
 * - Second stage: Uses the opposite architecture for balance
 * - Rgen flag: Controls which stage gets which architecture
 * - Width management: Handles precision across domain boundaries
 *
 * @tparam T Method type for sub-FFT implementations
 */
trait mixedComponents[T <: Method] extends streamedComponents with combine[T] {
    /** Architecture selection flag from decomposition parameters */
    val rgen = p.p.rgen

    /** Standard streaming I/O interface */
    val io = iogen

    /**
     * First stage processing block
     *
     * This block implements the first stage of the mixed-radix decomposition.
     * The implementation style (flat vs streamed) is determined by the rgen flag
     * to optimize resource usage and performance characteristics.
     *
     * Architecture Selection Logic:
     * - If rgen = true: Use streamed implementation for first stage
     * - If rgen = false: Use flat implementation with streaming adapter
     *
     * Width Configuration:
     * - Input width: dW (global input precision)
     * - Output width: interW (intermediate precision for second stage)
     *
     * Rationale:
     * The rgen flag typically indicates whether the first stage should use
     * resource-efficient (streamed) or performance-oriented (flat) implementation
     * based on the specific characteristics of the first stage transform size.
     */
    val f_block = FFTIOConfig(dW, interW) on new Area {
        /**
         * First stage processor with adaptive architecture
         *
         * Creates either a streamed or flat FFT processor based on rgen flag:
         * - Streamed: Direct instantiation of streamed implementation
         * - Flat: Flat implementation wrapped in streaming adapter
         */
        val u = if (rgen) {
            // Use streamed implementation for first stage
            val m = new Streamed(first_shift)
            m(first, "f")
        } else {
            // Use flat implementation with streaming adapter
            val m = new Flat(first_shift)
            new m.toStreamed(first, "s").asInstanceOf[streamedComponents]
        }

        /**
         * Delay through first stage
         *
         * The delay depends on the chosen implementation style and
         * the characteristics of the first stage transform.
         */
        val delay = u.delay
    }

    /**
     * Second stage processing block
     *
     * This block implements the second stage of the mixed-radix decomposition.
     * It uses the opposite architecture from the first stage to provide
     * balance and optimize overall system performance.
     *
     * Architecture Selection Logic:
     * - If rgen = true: Use flat implementation with streaming adapter
     * - If rgen = false: Use streamed implementation
     *
     * Width Configuration:
     * - Input width: interW (intermediate precision from first stage)
     * - Output width: oW (global output precision)
     *
     * Rationale:
     * By using opposite architectures for the two stages, the system
     * achieves a balance between resource usage and performance that
     * would not be possible with a uniform architecture approach.
     */
    val s_block = FFTIOConfig(interW, oW) on new Area {
        /**
         * Second stage processor with complementary architecture
         *
         * Creates the opposite architecture from the first stage:
         * - Flat with adapter: If first stage was streamed
         * - Streamed: If first stage was flat with adapter
         */
        val u = if (rgen) {
            // Use flat implementation with streaming adapter
            val m = new Flat(second_shift)
            new m.toStreamed(second, "s").asInstanceOf[streamedComponents]
        } else {
            // Use streamed implementation for second stage
            val m = new Streamed(second_shift)
            m(second, "s")
        }

        /**
         * Delay through second stage
         *
         * The delay depends on the chosen implementation style and
         * the characteristics of the second stage transform.
         */
        val delay = u.delay
    }
}